// 第 5 步：实现发送/接收循环（sendAndRecv）

// step 1:
// 在 tcp_socket.h 中写出必要的 #pragma once、依赖头文件、命名空间、以及 TCPBufferSize 常量。此时暂不定义结构体。
#pragma once

#include <cstddef>
#include <cstring>
#include <functional>
#include <netinet/in.h>
#include <string>
#include <vector>
#include "time_utils.h"
#include "logging.h"
#include "socket_utils.h"

namespace Common {
constexpr size_t TCPBufferSize = 64 * 1024 * 1024;

// step 2:
// 构造函数需要接收 Logger&，初始化收发缓冲区；还要显式禁止默认构造、拷贝/移动
struct TCPSocket {
  explicit TCPSocket(Logger &logger)
      : logger_(logger) {
    outbound_data_.resize(TCPBufferSize);
    inbound_data_.resize(TCPBufferSize);
  }

  // step 3
  // 在头文件中声明三个核心接口：connect、sendAndRecv、send，并加上注释说明职责
  auto connect(const std::string &ip, const std::string &iface, int port, bool is_listening);
  auto sendAndRecv() noexcept;
  auto send(const void *data, size_t len) noexcept;

  TCPSocket() = delete;
  TCPSocket(const TCPSocket &) = delete;
  TCPSocket(const TCPSocket &&) = delete;
  TCPSocket &operator=(const TCPSocket &) = delete;
  TCPSocket &operator=(const TCPSocket &&) = delete;

  int socket_fd_ = -1;
  std::vector<char> outbound_data_;
  size_t next_send_valid_index_ = 0;
  std::vector<char> inbound_data_;
  size_t next_rcv_valid_index_ = 0;
  struct sockaddr_in socket_attrib_{};
  std::function<void(TCPSocket *s, Nanos rx_time)> recv_callback_ = nullptr;
  std::string time_str_;
  Logger &logger_;
};

// step 4:
// 构造 SocketCfg，强制 needs_so_timestamp = true。
// 调用 createSocket 获取 socket 描述符。
// 初始化 socket_attrib_，方便后续 recvmsg 使用
auto TCPSocket::connect(const std::string &ip, const std::string &iface, int port, bool is_listening) {
  const SocketCfg socket_cfg{ip, iface, port, false, is_listening, true};
  socket_fd_ = createSocket(logger_, socket_cfg);

  socket_attrib_.sin_addr.s_addr = INADDR_ANY;
  socket_attrib_.sin_port = htons(port);
  socket_attrib_.sin_family = AF_INET;

  return socket_fd_;
}

// step 4:
// 准备 ctrl 控制缓冲区，接收 SCM_TIMESTAMP。
// 设置 iovec 与 msghdr，调用 recvmsg 非阻塞读取。
// 若读取成功，解析内核时间戳、记入日志、触发回调。
// 若发送缓冲区有数据，则调用 send 非阻塞写出，并记录日志。
// 最后根据是否读取到数据返回布尔值。
auto TCPSocket::sendAndRecv() noexcept{
  char ctrl[CMSG_SPACE(sizeof(struct timeval))];
  auto cmsg = reinterpret_cast<struct cmsghdr *>(&ctrl);

  iovec iov{inbound_data_.data() + next_rcv_valid_index_, TCPBufferSize - next_rcv_valid_index_};
  msghdr msg{&socket_attrib_, sizeof(socket_attrib_), &iov, 1, ctrl, sizeof(ctrl), 0};

  const auto read_size = recvmsg(socket_fd_, &msg, MSG_DONTWAIT);
  if (read_size > 0) {
    next_rcv_valid_index_ += read_size;

    Nanos kernel_time = 0;
    timeval time_kernel;
    if (cmsg->cmsg_level == SOL_SOCKET &&
        cmsg->cmsg_type == SCM_TIMESTAMP &&
        cmsg->cmsg_len == CMSG_LEN(sizeof(time_kernel))) {
      memcpy(&time_kernel, CMSG_DATA(cmsg), sizeof(time_kernel));
      kernel_time = time_kernel.tv_sec * NANOS_TO_SECS + time_kernel.tv_usec * NANOS_TO_MICROS;
    }

    const auto user_time = getCurrentNanos();
    logger_.log("%:% %() % read socket:% len:% utime:% ktime:% diff:%\n",
                __FILE__, __LINE__, __FUNCTION__,
                Common::getCurrentTimeStr(&time_str_),
                socket_fd_, next_rcv_valid_index_,
                user_time, kernel_time, (user_time - kernel_time));

    if (recv_callback_) {
      recv_callback_(this, kernel_time);
    }
  }

  if (next_send_valid_index_ > 0) {
    const auto n = ::send(socket_fd_, outbound_data_.data(),
                          next_send_valid_index_, MSG_DONTWAIT | MSG_NOSIGNAL);
    logger_.log("%:% %() % send socket:% len:%\n",
                __FILE__, __LINE__, __FUNCTION__,
                Common::getCurrentTimeStr(&time_str_),
                socket_fd_, n);
  }
  next_send_valid_index_ = 0;

  return (read_size > 0);
}
}  // namespace Common